﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Globalization;
using System.Reflection;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Xml;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.ApplicationRuntime;
using Microsoft.SharePoint.Utilities;

namespace SPVSX.Explorer.Commands.SharePoint
{
    /// <summary>
    /// Utilities
    /// </summary>
    public static class Utility
    {
        /// <summary>
        /// Is the url a javascript function
        /// </summary>
        /// <param name="url">The url</param>
        /// <returns>True if the url is javascript</returns>
        public static bool IsJavascriptUrl(string url)
        {
            return url.ToUpperInvariant().StartsWith("javascript:".ToUpperInvariant());
        }

        




        /// <summary>
        /// Is the current url a url containing layouts
        /// </summary>
        /// <param name="serverRelativeWebUrl">The url to interrogate</param>
        /// <returns>True if the url contains '/_layouts/'</returns>
        public static bool IsLayoutsUrl(string serverRelativeWebUrl)
        {
            return serverRelativeWebUrl.TrimStart(new char[] { '/' }).ToUpperInvariant().Contains("_layouts".ToUpperInvariant());
        }

        /// <summary>
        /// Get the <see cref="SPElementDefinition"/>s for all the feature which contain the element supplied
        /// </summary>
        /// <param name="culture">The <see cref="CultureInfo"/></param>
        /// <param name="featureCollection">The collection of <see cref="SPFeature"/>s to query</param>
        /// <param name="typeElementOfInterest">The name of the element type being queried for</param>
        /// <param name="features">The List containing the features of interest</param>
        public static void GetFeaturesWhichContainElementType(CultureInfo culture,
            SPFeatureCollection featureCollection,
            string typeElementOfInterest, ref List<SPElementDefinition> features)
        {
            foreach (SPFeature feature in featureCollection)
            {
                if (feature.Definition != null)
                {
                    SPFeatureDefinition featureDefinition = feature.Definition;
                    SPElementDefinitionCollection elementDefinitionCollection =
                        featureDefinition.GetElementDefinitions(System.Globalization.CultureInfo.InvariantCulture);

                    if (elementDefinitionCollection != null)
                    {
                        foreach (SPElementDefinition elementDefinition in elementDefinitionCollection)
                        {
                            if (elementDefinition.ElementType == typeElementOfInterest)
                            {
                                if (elementDefinition.FeatureDefinition.SupportsLanguage(culture))
                                {
                                    features.Add(elementDefinition);
                                }
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Get the attribute value from the supplied xml
        /// </summary>
        /// <param name="xmlNode">The <see cref="XmlNode"/> containing the data</param>
        /// <param name="attribute">The attribute name</param>
        /// <returns>The value</returns>
        public static string XmlGetAttributeValue(XmlNode xmlNode, string attribute)
        {
            XmlAttribute xmlAttribute = xmlNode.Attributes[attribute];
            if (xmlAttribute == null)
            {
                return null;
            }
            return xmlAttribute.Value;
        }

        /// <summary>
        /// Get the attribute value as an integer from the supplied xml
        /// </summary>
        /// <param name="xmlNode">The <see cref="XmlNode"/> containing the data</param>
        /// <param name="attribute">The attribute name</param>
        /// <param name="defaultNumber">The default number</param>
        /// <returns>The value</returns>
        public static int XmlGetAttributeValueAsInt32(XmlNode xmlNode, string attribute, int defaultNumber)
        {
            XmlAttribute xmlAttribute = xmlNode.Attributes[attribute];
            int num = defaultNumber;
            if (xmlAttribute != null)
            {
                try
                {
                    num = int.Parse(xmlAttribute.Value, NumberFormatInfo.InvariantInfo);
                }
                catch (ArgumentNullException)
                {
                }
                catch (FormatException)
                {
                }
                catch (OverflowException)
                {
                }
            }
            return num;
        }

        /// <summary>
        /// Get the attribute value from the supplied xml
        /// </summary>
        /// <param name="xmlNode">The <see cref="XmlNode"/> containing the data</param>
        /// <param name="attribute">The attribute name</param>
        /// <returns>The value</returns>
        public static bool XmlGetAttributeValueAsBoolean(XmlNode xmlNode, string attribute)
        {
            XmlAttribute xmlAttribute = xmlNode.Attributes[attribute];
            bool flag = false;
            if (xmlAttribute != null)
            {
                try
                {
                    flag = bool.Parse(xmlAttribute.Value);
                }
                catch (ArgumentNullException)
                {
                }
                catch (FormatException)
                {
                }
            }
            return flag;
        }

        /// <summary>
        /// Get the attribute value from the supplied xml
        /// </summary>
        /// <param name="xmlNode">The <see cref="XmlNode"/> containing the data</param>
        /// <param name="attribute">The attribute name</param>
        /// <returns>The value</returns>
        public static SPBasePermissions XmlGetAttributeValueAsPermissions(XmlNode xmlNode, string attribute)
        {
            XmlAttribute xmlAttribute = xmlNode.Attributes[attribute];
            SPBasePermissions emptyMask = SPBasePermissions.EmptyMask;
            if (xmlAttribute != null)
            {
                try
                {
                    emptyMask = (SPBasePermissions)Enum.Parse(typeof(SPBasePermissions), xmlAttribute.Value, true);
                }
                catch (ArgumentNullException)
                {
                }
                catch (ArgumentException)
                {
                }
            }
            return emptyMask;
        }

        /// <summary>
        /// Is the passed url and absolute url. Ie does it start http or https
        /// </summary>
        /// <param name="url">The url</param>
        /// <returns>True is HTTP or HTTPS is found at the start of the url</returns>
        public static bool IsAbsoluteUrl(string url)
        {
            return (url.ToLower().StartsWith("http") || url.ToLower().StartsWith("https"));
        }

        /// <summary>
        /// Validate the format of the passed url as valid. Will check the protocol
        /// </summary>
        /// <param name="url">The url to check</param>
        /// <param name="allowRelativeUrl">Is the url relative, true indicates it is</param>
        /// <returns>The validation result</returns>
        public static bool IsUrlValid(string url, bool allowRelativeUrl)
        {
            if (String.IsNullOrEmpty(url))
            {
                return false;
            }
            if (!string.IsNullOrEmpty(url))
            {
                if (url.StartsWith(@"\\") || url.StartsWith("//"))
                {
                    url = "file:" + url;
                }
            }
            else
            {
                return false;
            }
            if (url.StartsWith("file:"))
            {
                url = url.Replace(@"\", "/");
            }
            if (!SPUrlUtility.IsProtocolAllowed(url, allowRelativeUrl) && !url.StartsWith("/"))
            {
                return false;
            }
            if (!SPUrlUtility.IsUrlFull(url))
            {
                if (allowRelativeUrl)
                {
                    return SPUrlUtility.IsUrlRelative(url);
                }
                return false;
            }
            return true;
        }

        /// <summary>
        /// Append a slash to 
        /// </summary>
        /// <param name="path">The path</param>
        /// <returns>If the string didn't start with slash</returns>
        public static string AppendLeadingSlash(string path)
        {
            if (path == null)
            {
                return null;
            }
            int length = path.Length;
            if ((length != 0) && (!path.StartsWith("/")))
            {
                path = '/' + path;
            }
            return path;
        }




        #region Resources

        /// <summary>
        /// Get the resource string from a resx file located in the 
        /// </summary>
        /// <param name="resource">The resource name</param>
        /// <returns>The value from the resource file</returns>
        public static string GetLocalisedStringFromHiveResources(string resource)
        {
            //If not a resources based value return the supplied value without modifications
            if (!resource.StartsWith("$Resources:"))
            {
                return resource;
            }

            string resourceFile;
            string resourceKeyName;

            if (resource.Contains(","))
            {
                string[] resourceElements = resource.Split(",".ToCharArray(),
                                                                   StringSplitOptions.RemoveEmptyEntries);
                if (resourceElements.Length > 1)
                {
                    resourceFile = resource;
                    resourceKeyName = resourceElements[1];
                }
                else
                {
                    resourceFile = resource;
                    resourceKeyName = resource.Replace("$Resources:", "");
                }
            }
            else
            {
                resourceFile = resource;
                resourceKeyName = resource.Replace("$Resources:", "");
            }
            uint language = SPContext.Current.Web != null ? SPContext.Current.Web.Language : 1033;

            return SPUtility.GetLocalizedString(resourceFile, resourceKeyName, language);
        }

        /// <summary>
        /// Get the resource string from a resx file located in the 
        /// </summary>
        /// <param name="resource">The resource name</param>
        /// <param name="resourceFileName">The resource file name</param>
        /// <returns>The value from the resource file</returns>
        public static string GetLocalisedStringFromHiveResources(string resource, string resourceFileName)
        {
            //If not a resources based value return the supplied value without modifications
            if (!resource.StartsWith("$Resources:"))
            {
                return resource;
            }

            string resourceFile;
            string resourceKeyName;

            if (resource.Contains(","))
            {
                string[] resourceElements = resource.Split(",".ToCharArray(),
                                                                   StringSplitOptions.RemoveEmptyEntries);
                if (resourceElements.Length > 1)
                {
                    resourceFile = resource;
                    resourceKeyName = resourceElements[1];
                }
                else
                {
                    resourceFile = resource;
                    resourceKeyName = resource.Replace("$Resources:", "");
                }
            }
            else
            {
                resourceKeyName = resource.Replace("$Resources:", "");
                resourceFile = "$Resources:" + resourceFileName + "," + resourceKeyName;
            }
            uint language = SPContext.Current.Web != null ? SPContext.Current.Web.Language : 1033;

            return SPUtility.GetLocalizedString(resourceFile, resourceKeyName, language);
        }

        #endregion

        #region Query String Helpers

        /// <summary>
        /// Gets the current query string removing given key value pairs.
        /// </summary>
        /// <param name="queryString">The <see cref="NameValueCollection"/> from the request query string</param>
        /// <param name="ignoreKeys">An array of keys to ignore</param>
        /// <returns>The query string</returns>
        public static string GetQueryString(NameValueCollection queryString,
                                            params string[] ignoreKeys)
        {
            StringBuilder query = new StringBuilder();

            foreach (string key in queryString)
            {
                bool ignore = false;
                foreach (string ignoreKey in ignoreKeys)
                {
                    if (!String.IsNullOrEmpty(ignoreKey) &&
                        string.Compare(key,
                                       ignoreKey,
                                       true) == 0)
                    {
                        ignore = true;
                    }
                }
                if (!ignore)
                {
                    query.Append("&");
                    query.Append(key);
                    query.Append("=");
                    query.Append(queryString[key]);
                }
            }

            if (query.Length > 0)
            {
                query.Remove(0,
                             1);
            }
            return query.ToString();
        }

        /// <summary>
        /// Set the query string.
        /// </summary>
        /// <param name="queryString">The <see cref="NameValueCollection"/> from the request query string</param>
        /// <param name="absoluteUrl">The absolute url</param>
        /// <param name="keyName">The key name</param>
        /// <param name="keyValue">The key value</param>
        /// <returns>The new full url</returns>
        public static string SetQueryString(NameValueCollection queryString,
                                            string absoluteUrl,
                                            string keyName,
                                            string keyValue)
        {
            StringBuilder newQueryString = new StringBuilder(absoluteUrl);
            newQueryString.Append("?");

            if (queryString.Count > 0)
            {
                newQueryString.Append(GetQueryString(queryString,
                                                     new[] { keyName }));
                newQueryString.Append("&");
            }

            newQueryString.Append(string.Format("{0}={1}",
                                                keyName,
                                                keyValue));

            return newQueryString.ToString();
        }

        /// <summary>
        /// Append supplied keys into the existing query string.
        /// </summary>
        /// <param name="queryString">The <see cref="NameValueCollection"/> containing the existing key value pairs</param>
        /// <param name="absoluteUrl">The absolute url</param>
        /// <param name="keysGroup">The key value pairs to append to the existing query string</param>
        /// <param name="replaceExistingKeyValues">Flag to indicate how existing keys are handled</param>
        /// <returns>The full url with query string attached</returns>
        public static string AppendKeysToExistingQueryString(NameValueCollection queryString,
                                                             string absoluteUrl,
                                                             Dictionary<string, string> keysGroup,
                                                             bool replaceExistingKeyValues)
        {
            #region Guard

            //Check method input
            if (String.IsNullOrEmpty(absoluteUrl))
            {
                throw new ArgumentException("Resources.QueryStringHelper_ArgumentException_AbsoluteUrl",
                                            "absoluteUrl");
            }

            if (queryString == null)
            {
                throw new ArgumentException("Resources.QueryStringHelper_ArgumentException_NameValueCollection",
                                            "queryString");
            }

            #endregion

            Dictionary<string, string> newQueryStringDict = new Dictionary<string, string>();

            //Copy the existing collection into the new before processing begins
            foreach (string key in queryString)
            {
                if (!string.IsNullOrEmpty(key))
                {
                    newQueryStringDict.Add(key,
                                           queryString[key]);
                }
            }

            //Loop all the keys in the new group
            foreach (string key in keysGroup.Keys)
            {
                //Decide whether to add or overwrite the value
                if (newQueryStringDict.ContainsKey(key))
                {
                    if (replaceExistingKeyValues)
                    {
                        newQueryStringDict[key] = keysGroup[key];
                    }
                }
                else
                {
                    newQueryStringDict.Add(key,
                                           keysGroup[key]);
                }
            }

            return AssembleUrlAndQueryString(absoluteUrl,
                                             newQueryStringDict);
        }

        /// <summary>
        /// Assemble a url and query string.
        /// </summary>
        /// <param name="absoluteUrl">The absolute url.</param>
        /// <param name="queryString">The dictionary containing the querystring key value pairs.</param>
        /// <returns>The assembled string.</returns>
        /// <exception cref="ArgumentException">Thrown if either input params are null or the url is an empty string.</exception>
        public static string AssembleUrlAndQueryString(string absoluteUrl,
                                                       Dictionary<string, string> queryString)
        {
            #region Guard

            //Check method input
            if (String.IsNullOrEmpty(absoluteUrl))
            {
                throw new ArgumentException("Resources.QueryStringHelper_ArgumentException_AbsoluteUrl",
                                            "absoluteUrl");
            }

            if (queryString == null)
            {
                throw new ArgumentException("Resources.QueryStringHelper_ArgumentException_QueryString",
                                            "queryString");
            }

            #endregion

            //Assign the url
            StringBuilder newQueryString = new StringBuilder(absoluteUrl);

            //Check the QS is actually valid
            if (queryString.Count > 0)
            {
                //Append the ? to the url
                newQueryString.Append("?");

                int keyCount = 0;

                foreach (KeyValuePair<string, string> item in queryString)
                {
                    keyCount++;
                    newQueryString.Append(HttpUtility.HtmlEncode(item.Key));
                    newQueryString.Append("=");
                    newQueryString.Append(HttpUtility.HtmlEncode(item.Value));
                    if (keyCount < queryString.Count)
                    {
                        newQueryString.Append("&");
                    }
                }
            }

            //Return the assembled url
            return newQueryString.ToString();
        }

        #endregion


        /// <summary>
        /// Associates a submit button to a textbox when hitting enter in the textbox
        /// </summary>
        /// <param name="webControl">WebControl to associate the submit button</param>
        /// <param name="button">Button control to submit when enter is pressed in the textbox</param>
        public static void SetDefaultButton(WebControl webControl, WebControl button)
        {
            if (webControl == null || !webControl.Visible)
            {
                return;
            }

            if (button == null || !button.Visible)
            {
                return;
            }

            webControl.Attributes.Add("onkeypress", "checkEnter('" + button.ClientID.ToString() + "', event);");
            StringBuilder sb = new StringBuilder();
            GetCheckEnterScript(sb);
            webControl.Page.ClientScript.RegisterClientScriptBlock(webControl.Page.GetType(), "CheckEnter", sb.ToString(), true);
        }

        /// <summary>
        /// Generate a checkEnter function to fire button submit when enter hit in textbox
        /// </summary>
        /// <param name="sb">Receive stringbuilder to build script</param>
        private static void GetCheckEnterScript(StringBuilder sb)
        {
            sb.Append("<script type=\"text/javascript\">");
            sb.Append("function checkEnter(btn, event)");
            sb.Append("{");
            sb.Append("    if (document.all)");
            sb.Append("    {");
            sb.Append("        if (event.keyCode == 13)");
            sb.Append("        {");
            sb.Append("            var o = document.getElementById(btn);");
            sb.Append("            event.returnValue=false;");
            sb.Append("            event.cancel = true;");
            sb.Append("            o.click();");
            sb.Append("        }");
            sb.Append("    }");
            sb.Append("    else if (document.getElementById)");
            sb.Append("    {");
            sb.Append("        if (event.which == 13)");
            sb.Append("        {");
            sb.Append("            var o = document.getElementById(btn);");
            sb.Append("            event.returnValue=false;");
            sb.Append("            event.cancel = true;");
            sb.Append("            o.click();");
            sb.Append("        }");
            sb.Append("    }");
            sb.Append("    else if (document.layers)");
            sb.Append("    {");
            sb.Append("        if(event.which == 13)   ");
            sb.Append("        {");
            sb.Append("            var o = document.getElementById(btn);");
            sb.Append("            event.returnValue=false;");
            sb.Append("            event.cancel = true;");
            sb.Append("            o.click();");
            sb.Append("        }");
            sb.Append("    }");
            sb.Append("}");
            sb.Append("</script>");
        }

        /// <summary>
        /// Copied from Reflector
        /// Dissassembled from SPUtility.ConvertMultiColumnValueToString() [internal]
        /// </summary>
        /// <param name="subColumnValues">A stronly typed list of string values from a Multi-Lookup or Multi-User field</param>
        /// <param name="bAddLeadingTailingDelimiter">Whether to add a trailing delimiter</param>
        /// <returns>a string value that can be fed into an SPMultiLookup or SPMultiUser field</returns>
        public static string ConvertMultiColumnValueToString(List<string> subColumnValues, bool bAddLeadingTailingDelimiter)
        {
            string strReturn = "";

            foreach (string strItem in subColumnValues)
            {
                strReturn += strItem + ";#";
            }

            if (!bAddLeadingTailingDelimiter)
            {
                strReturn = strReturn.Substring(0, strReturn.Length - 2);
            }

            return strReturn;

            //bool flag = false;
            //StringBuilder builder = new StringBuilder(0xff);
            //int num = 0;
            //while (true)
            //{
            //    string str;
            //    if (num < subColumnValues.Count)
            //    {
            //        str = subColumnValues[num];
            //        if (!string.IsNullOrEmpty(str))
            //        {
            //            str = str.Replace(";", ";;");
            //        }
            //        if (!string.IsNullOrEmpty(str))
            //        {
            //            flag = true;
            //        }
            //        if (bAddLeadingTailingDelimiter || (num != 0))
            //        {
            //            builder.Append(";#");
            //        }
            //    }
            //    else
            //    {
            //        if (!flag)
            //        {
            //            return string.Empty;
            //        }
            //        if (bAddLeadingTailingDelimiter)
            //        {
            //            builder.Append(";#");
            //        }
            //        return builder.ToString();
            //    }
            //    builder.Append(str);
            //    num++;
            //}
        }

        public static string ConvertPrincipalInfoToCommaSeparatedAccounts(IList<SPPrincipalInfo> principals)
        {
            string returnString = "";

            foreach (SPPrincipalInfo pInfo in principals)
            {
                returnString += pInfo.LoginName + ",";
            }

            return returnString;
        }

        /// <summary>
        /// Returns an SPFieldUserValue object from an SPPrincipalInfo object
        /// </summary>
        /// <param name="pInfo">The SPPRincipalInfo object</param>
        /// <param name="web">The SPWeb in which we are looking</param>
        /// <returns>An SPFieldUserValue object. Will be null if not found</returns>
        public static SPFieldUserValue ConvertPrincipalInfoToSPFieldUserValue(SPPrincipalInfo pInfo, SPWeb web)
        {
            string strValue = String.Empty;

            if (pInfo.IsSharePointGroup)
            {
                // all groups are stored in the groups collection
                SPGroup group = web.Groups.GetByID(pInfo.PrincipalId);

                strValue = group.ID + ";#" + group.Name;
            }
            else
            {
                SPUser user = web.EnsureUser(pInfo.LoginName);
                strValue = user.ID + ";#" + user.Name;
            }

            if (String.IsNullOrEmpty(strValue))
            {
                return null;
            }
            else
            {
                return new SPFieldUserValue(web, strValue);
            }
        }

        public static List<string> ConvertPrincipalInfoToStringList(IList<SPPrincipalInfo> principals, SPWeb web)
        {
            List<string> returnStrings = new List<string>();

            foreach (SPPrincipalInfo pInfo in principals)
            {
                if (pInfo.IsSharePointGroup)
                {
                    #region Find SharePoint Group
                    // all groups are stored in the groups collection
                    SPGroup group = web.Groups.GetByID(pInfo.PrincipalId);

                    returnStrings.Add(group.ID + ";#" + group.Name);
                    #endregion
                }
                else
                {
                    #region Find Account
                    // the user account (or domain group) will not be in "AllUsers"
                    // we need to look in the User Information List
                    SPUser user = web.EnsureUser(pInfo.LoginName);
                    returnStrings.Add(user.ID + ";#" + user.Name);
                    #endregion
                }
            }

            return returnStrings;
        }

    }


}